package com.yunwaikeji.gy_tool_library.utils.display;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.StateListDrawable;
import android.os.Build;
import android.util.Log;

import com.yunwaikeji.gy_tool_library.info.GYBaseInfo;
import com.yunwaikeji.gy_tool_library.info.GYDisplayInfo;

public class GYDisplayBuilder implements GYBaseInfo {
    private void error(String error) {
        Log.e("GYDisplayBuilder", error);
    }

    private float widthPx = WRAP_CONTENT;
    private float heightPx = WRAP_CONTENT;
    private float radiusLeftTopPx = 0;
    private float radiusRightTopPx = 0;
    private float radiusRightBottomPx = 0;
    private float radiusLeftBottomPx = 0;
    private Drawable normalDrawable;
    private Drawable changeDrawable;
    private float edgeWidthPx = 0;
    private int edgeColor = Color.LTGRAY;
    private int displayStyle = GYDisplayInfo.DisplayStyle.NONE;
    private boolean edgeOver = false;//边框是否覆盖
    private int changeEffect = GYDisplayInfo.ChangeEffect.NONE;
    /**
     * 旋转角度
     */
    private int rotateDegree = 0;
    private boolean flip;

    public GYDisplayBuilder setWidthPx(float widthPx) {
        return setSizePx(widthPx, heightPx);
    }

    public GYDisplayBuilder setWidthDp(Context context, float widthDp) {
        return setWidthPx(GYDisplayUtils.dp2px(context, widthDp));
    }

    public GYDisplayBuilder setHeightPx(float heightPx) {
        return setSizePx(widthPx, heightPx);
    }

    public GYDisplayBuilder setHeightPx(Context context, float heightDp) {
        return setHeightPx(GYDisplayUtils.dp2px(context, heightDp));
    }

    public GYDisplayBuilder setSizePx(float widthPx, float heightPx) {
        this.widthPx = widthPx;
        this.heightPx = heightPx;
        return this;
    }

    public GYDisplayBuilder setSizeDp(Context context, float widthDp, float heightDp) {
        return setSizePx(GYDisplayUtils.dp2px(context, widthDp), GYDisplayUtils.dp2px(context, heightDp));
    }

    public GYDisplayBuilder setRadiusPx(int radiusPx) {
        setRadiusLeftTopPx(radiusPx);
        setRadiusRightTopPx(radiusPx);
        setRadiusRightBottomPx(radiusPx);
        setRadiusLeftBottomPx(radiusPx);
        return this;
    }

    public GYDisplayBuilder setRadiusDp(Context context, int radiusDp) {
        setRadiusLeftTopDp(context, radiusDp);
        setRadiusRightTopDp(context, radiusDp);
        setRadiusRightBottomDp(context, radiusDp);
        setRadiusLeftBottomDp(context, radiusDp);
        return this;
    }

    public GYDisplayBuilder setRadiusLeftTopPx(float radiusLeftTopPx) {
        this.radiusLeftTopPx = radiusLeftTopPx;
        return this;
    }

    public GYDisplayBuilder setRadiusLeftTopDp(Context context, int radiusLeftTopDp) {
        return setRadiusLeftTopPx(GYDisplayUtils.dp2px(context, radiusLeftTopDp));
    }

    public GYDisplayBuilder setRadiusRightTopPx(float radiusRightTopPx) {
        this.radiusRightTopPx = radiusRightTopPx;
        return this;
    }

    public GYDisplayBuilder setRadiusRightTopDp(Context context, int radiusRightTopDp) {
        return setRadiusRightTopPx(GYDisplayUtils.dp2px(context, radiusRightTopDp));
    }

    public GYDisplayBuilder setRadiusRightBottomPx(float radiusRightBottomPx) {
        this.radiusRightBottomPx = radiusRightBottomPx;
        return this;
    }

    public GYDisplayBuilder setRadiusRightBottomDp(Context context, int radiusRightBottomDp) {
        return setRadiusRightBottomPx(GYDisplayUtils.dp2px(context, radiusRightBottomDp));
    }

    public GYDisplayBuilder setRadiusLeftBottomPx(float radiusLeftBottomPx) {
        this.radiusLeftBottomPx = radiusLeftBottomPx;
        return this;
    }

    public GYDisplayBuilder setRadiusLeftBottomDp(Context context, int radiusLeftBottomDp) {
        return setRadiusLeftBottomPx(GYDisplayUtils.dp2px(context, radiusLeftBottomDp));
    }

    public GYDisplayBuilder setNormal(Drawable normalDrawable) {
        this.normalDrawable = normalDrawable;
        return this;
    }

    public GYDisplayBuilder setNormal(Bitmap normalBitmap) {
        return setNormal(GYDisplayUtils.toDrawable(normalBitmap));
    }

    public GYDisplayBuilder setNormalColor(int backgroundColor) {
        GradientDrawable gradientDrawable = new GradientDrawable();
        gradientDrawable.setColor(backgroundColor);
        return setNormal(gradientDrawable);
    }

    public GYDisplayBuilder setNormalColor(GradientDrawable.Orientation orientation, int... backgroundColor) {
        return setNormal(new GradientDrawable(orientation, backgroundColor));
    }

    public GYDisplayBuilder setChange(Drawable changeDrawable) {
        this.changeDrawable = changeDrawable;
        return this;
    }

    public GYDisplayBuilder setChange(Bitmap changeBitmap) {
        return setChange(GYDisplayUtils.toDrawable(changeBitmap));
    }

    public GYDisplayBuilder setEdgeWidthPx(float edgeWidthPx) {
        this.edgeWidthPx = edgeWidthPx;
        return this;
    }

    public GYDisplayBuilder setEdgeWidthDp(Context context, float edgeWidthDp) {
        return setEdgeWidthPx(GYDisplayUtils.dp2px(context, edgeWidthDp));
    }

    public GYDisplayBuilder setEdgeColor(int edgeColor) {
        this.edgeColor = edgeColor;
        return this;
    }

    public GYDisplayBuilder setDisplayStyle(@GYDisplayInfo.DisplayStyle int displayStyle) {
        this.displayStyle = displayStyle;
        return this;
    }

    public GYDisplayBuilder setChangeEffect(@GYDisplayInfo.ChangeEffect int changeEffect) {
        this.changeEffect = changeEffect;
        return this;
    }

    public GYDisplayBuilder setEdgeOver(boolean edgeOver) {
        this.edgeOver = edgeOver;
        return this;
    }

    /**
     * 旋转角度
     *
     * @param rotateDegree
     * @return
     */
    public GYDisplayBuilder setRotateDegree(int rotateDegree) {
        this.rotateDegree = rotateDegree;
        return this;
    }

    /**
     * 翻转
     */
    public GYDisplayBuilder setFlip(boolean flip) {
        this.flip = flip;
        return this;
    }

    public Drawable buildDrawable() {
        if (normalDrawable == null) {
            normalDrawable = new ColorDrawable(Color.TRANSPARENT);
        }
        if (changeDrawable == null) {
            changeDrawable = normalDrawable;
        }
        normalDrawable = getDrawable(normalDrawable);
        changeDrawable = getDrawable(changeDrawable);
        StateListDrawable stateListDrawable = new StateListDrawable();
        Drawable drawable = null;
        switch (changeEffect) {
            case GYDisplayInfo.ChangeEffect.PRESS:
                stateListDrawable.addState(new int[]{-android.R.attr.state_pressed}, normalDrawable);
                stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, changeDrawable);
                break;
            case GYDisplayInfo.ChangeEffect.CHECK:
                stateListDrawable.addState(new int[]{-android.R.attr.state_checked}, normalDrawable);
                stateListDrawable.addState(new int[]{android.R.attr.state_checked}, changeDrawable);
                break;
            case GYDisplayInfo.ChangeEffect.NONE:
            default:
                drawable = normalDrawable;
        }
        if (drawable == null) {
            drawable = stateListDrawable;
        }
        if (rotateDegree > 0 && flip) {
            Matrix matrix = new Matrix();
            matrix.postRotate(rotateDegree);
            matrix.postScale(-1, 1);
            Bitmap bitmap = Bitmap.createBitmap(GYDisplayUtils.toBitmap(drawable), 0, 0, (int) widthPx, (int) heightPx, matrix, true);
            return GYDisplayUtils.toDrawable(bitmap);
        }
        return drawable;
    }

    public Bitmap buildBitmap() {
        return GYDisplayUtils.toBitmap(buildDrawable());
    }

    private Drawable getDrawable(Drawable drawable) {
        if (drawable != null) {
            if (drawable instanceof StateListDrawable) {
                //selector图
                StateListDrawable stateListDrawable = (StateListDrawable) drawable;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
                    StateListDrawable stateListDrawable1 = new StateListDrawable();
                    for (int i = 0; i < stateListDrawable.getStateCount(); i++) {
                        Drawable drawable1 = stateListDrawable.getStateDrawable(i);
                        stateListDrawable1.addState(drawable1.getState(), getDrawable(drawable1));
                    }
                    return stateListDrawable1;
                } else {
                    error("Api小于29的版本不支持getStateCount和getStateDrawable方法");
                }
                return stateListDrawable;
            } else if (drawable instanceof GradientDrawable) {
                //shape图
                return getGradientDrawable(drawable);
            } else if (drawable instanceof ColorDrawable) {
                //颜色图
                return getColorDrawable(drawable);
            } else {
                //资源图片
                return getIconDrawable(drawable);
            }
        }
        return null;
    }

    private Drawable getGradientDrawable(Drawable drawable) {
        GradientDrawable gradientDrawable = (GradientDrawable) drawable;
        float width = gradientDrawable.getIntrinsicWidth();
        float height = gradientDrawable.getIntrinsicHeight();
        if (widthPx != WRAP_CONTENT && (widthPx == MATCH_PARENT || widthPx >= 0)) {
            width = widthPx;
        }
        if (heightPx != WRAP_CONTENT && (heightPx == MATCH_PARENT || heightPx >= 0)) {
            height = heightPx;
        }
        gradientDrawable.setSize((int) width, (int) height);
        if (radiusLeftTopPx > 0 || radiusRightTopPx > 0 || radiusRightBottomPx > 0 || radiusLeftBottomPx > 0) {
            gradientDrawable.setCornerRadii(new float[]{radiusLeftTopPx, radiusLeftTopPx, radiusRightTopPx, radiusRightTopPx, radiusRightBottomPx, radiusRightBottomPx, radiusLeftBottomPx, radiusLeftBottomPx});
        }
        if (edgeWidthPx > 0) {
            gradientDrawable.setStroke((int) edgeWidthPx, edgeColor);
        }
        switch (displayStyle) {
            case GYDisplayInfo.DisplayStyle.NONE:
            default:
                gradientDrawable.setShape(GradientDrawable.RECTANGLE);
                break;
            case GYDisplayInfo.DisplayStyle.CIRCLE_START:
            case GYDisplayInfo.DisplayStyle.CIRCLE_CENTER:
            case GYDisplayInfo.DisplayStyle.CIRCLE_END:
            case GYDisplayInfo.DisplayStyle.OVAL:
                gradientDrawable.setShape(GradientDrawable.OVAL);
                break;
        }
        return gradientDrawable;
    }

    private Drawable getIconDrawable(Drawable drawable) {
        if (drawable != null && drawable.getIntrinsicWidth() > 0 && drawable.getIntrinsicHeight() > 0) {
            Bitmap bitmap = GYDisplayUtils.toBitmap(drawable);

            float width;
            float height;

            Bitmap output;
            Canvas canvas;
            Paint paint = new Paint();
            paint.setAntiAlias(true);
            paint.setColor(0xff424242);

            Rect src = new Rect();
            Rect dst = new Rect();

            if (widthPx > 0 && heightPx > 0) {
                width = widthPx;
                height = heightPx;
                Bitmap bitmap2 = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
                Canvas canvas2 = new Canvas(bitmap2);
                dst.set(0, 0, (int) width, (int) height);
                canvas2.drawBitmap(bitmap, null, dst, paint);
                bitmap = bitmap2;
            } else if (widthPx == 0 || heightPx == 0) {
                return new ColorDrawable();
            } else {
                width = bitmap.getWidth();
                height = bitmap.getHeight();
            }

            float edgeWidth = !edgeOver ? edgeWidthPx : 0;

            final float minSize = Math.min(width, height);
            final float maxSize = Math.max(width, height);
            final float radius = minSize / 2;

            float left = 0;
            float top = 0;
            float right = width;
            float bottom = height;
            int index;
            switch (displayStyle) {
                case GYDisplayInfo.DisplayStyle.NONE:
                    index = 0;
                    output = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
                    canvas = new Canvas(output);

                    canvas.drawPath(pathRadius(width, height, edgeWidthPx / 2), paint);
                    if (edgeOver) {
                        src.set((int) edgeWidthPx / 2, (int) edgeWidthPx / 2, (int) (width - edgeWidthPx / 2), (int) (height - edgeWidthPx / 2));
                        dst.set(src);
                    } else {
                        src.set(0, 0, (int) width, (int) height);
                        dst.set((int) edgeWidthPx / 2, (int) edgeWidthPx / 2, (int) (width - edgeWidthPx / 2), (int) (height - edgeWidthPx / 2));
                    }
                    break;
                case GYDisplayInfo.DisplayStyle.CIRCLE_START:
                case GYDisplayInfo.DisplayStyle.CIRCLE_CENTER:
                case GYDisplayInfo.DisplayStyle.CIRCLE_END:
                    index = 1;
                    output = Bitmap.createBitmap((int) maxSize, (int) maxSize, Bitmap.Config.ARGB_8888);
                    canvas = new Canvas(output);
                    canvas.drawARGB(0, 0, 0, 0);
                    switch (displayStyle) {
                        case GYDisplayInfo.DisplayStyle.CIRCLE_CENTER:
                            if (width < height) {
                                top = height / 2 - radius;
                                bottom = top + minSize;
                            } else if (width > height) {
                                left = width / 2 - radius;
                                right = left + minSize;
                            }
                            break;
                        case GYDisplayInfo.DisplayStyle.CIRCLE_START:
                            if (width < height) {
                                bottom = minSize;
                            } else if (width > height) {
                                right = minSize;
                            }
                            break;
                        case GYDisplayInfo.DisplayStyle.CIRCLE_END:
                            if (width < height) {
                                top = height - minSize;
                            } else if (width > height) {
                                left = width - minSize;
                            }
                            break;
                    }
                    src.set((int) left, (int) top, (int) right, (int) bottom);
                    dst.set((int) edgeWidth, (int) edgeWidth, (int) (maxSize - edgeWidth), (int) (maxSize - edgeWidth));
                    canvas.drawCircle(maxSize / 2, maxSize / 2, maxSize / 2 - edgeWidth, paint);
                    break;
                case GYDisplayInfo.DisplayStyle.OVAL:
                    index = 2;
                    output = Bitmap.createBitmap((int) width, (int) height, Bitmap.Config.ARGB_8888);
                    canvas = new Canvas(output);
                    canvas.drawARGB(0, 0, 0, 0);
                    src.set(0, 0, (int) width, (int) height);
                    dst.set((int) edgeWidth, (int) edgeWidth, (int) (width - edgeWidth), (int) (height - edgeWidth));
                    canvas.drawOval(new RectF(dst), paint);
                    break;
                default:
                    return drawable;
            }

            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
            canvas.drawBitmap(bitmap, src, dst, paint);

            paint.setXfermode(null);

            if (edgeWidthPx != 0) {
                // 将图片加边框
                Paint paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
                paint1.setColor(edgeColor);
                paint1.setStrokeWidth(edgeWidthPx);
                paint1.setStyle(Paint.Style.STROKE);
                paint1.setAntiAlias(true);
                switch (index) {
                    case 0:
                        canvas.drawPath(pathRadius(width, height, edgeWidthPx / 2), paint1);
                        break;
                    case 1:
                        canvas.drawCircle(radius, radius, radius - edgeWidthPx / 2, paint1);
                        break;
                    case 2:
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            canvas.drawOval(edgeWidthPx / 2, edgeWidthPx / 2, widthPx - edgeWidthPx / 2, height - edgeWidthPx / 2, paint1);
                        } else {
                            error("Api小于21的版本不支持canvas.drawOval方法");
                        }
                        break;
                }
            }

            if (!bitmap.isRecycled()) {
                bitmap.recycle();
            }

            drawable = new BitmapDrawable(output);
        }
        return drawable;
    }

    private Drawable getColorDrawable(Drawable drawable) {
        ColorDrawable colorDrawable = (ColorDrawable) drawable;
        GradientDrawable gradientDrawable = new GradientDrawable();
        gradientDrawable.setColor(colorDrawable.getColor());
        return getGradientDrawable(gradientDrawable);
    }

    private Path pathRadius(float width, float height, float edgeWidthPx) {
        return GYDisplayUtils.newPathBuilder(width, height)
                .setRadiusLeftTopPx(radiusLeftTopPx)
                .setRadiusRightTopPx(radiusRightTopPx)
                .setRadiusLeftBottomPx(radiusLeftBottomPx)
                .setRadiusRightBottomPx(radiusRightBottomPx)
                .setEdgeWidthPx(edgeWidthPx)
                .build();
    }
}
